#include "byte_stream.hh"
#include <iostream>

#define SMALL_CAP (1 << 10)
#define MIN(x,y) (x > y? y : x)
// Dummy implementation of a flow-controlled in-memory byte stream.

// For Lab 0, please replace with a real implementation that passes the
// automated checks run by `make check_lab0`.

// You will need to add private members to the class declaration in `byte_stream.hh`

template <typename... Targs>
void DUMMY_CODE(Targs &&... /* unused */) {}

using namespace std;

ByteStream::ByteStream(const size_t capacity):
    _total_written(0), _total_read(0), _input_ended(false) {
    if(capacity <= 0) {
        set_error();
    }else{
        _stream.capacity = capacity;
        _stream.buf.reserve(MIN(capacity, SMALL_CAP));
    }
}

size_t ByteStream::write(const string &data) {
    if(input_ended())
        return 0;
    size_t &&dataSize = data.size();
    size_t &&remain = remaining_capacity();
    size_t count = MIN(remain, dataSize);
    if(count){
        _stream.buf.insert(_stream.buf.end(), data.begin(), data.begin() + count);
        _total_written += count;
    }

    return count;
}

//! \param[in] len bytes will be copied from the output side of the buffer
string ByteStream::peek_output(const size_t len) const {
    return string(_stream.buf.begin(), _stream.buf.begin() + MIN(len, _stream.buf.size()));
}

//! \param[in] len bytes will be removed from the output side of the buffer
void ByteStream::pop_output(const size_t len) {
    size_t &&bufSize = _stream.buf.size();
    if(bufSize <= len) {
        _stream.buf.clear();
        _total_read += bufSize;
    } else {
        _stream.buf.assign(_stream.buf.data() + len, _stream.buf.data() + _stream.buf.size());
        _total_read += len;
    }
}

//! Read (i.e., copy and then pop) the next "len" bytes of the stream
//! \param[in] len bytes will be popped and returned
//! \returns a string
std::string ByteStream::read(const size_t len) {
    if(eof())
        return string();
    int &&count = MIN(len, _stream.buf.size());
    string &&str = peek_output(count);
    pop_output(count);

    return move(str);
}

void ByteStream::end_input() {
    cerr << "\n********    going to close, remains are " << remaining_capacity() << endl;
    _input_ended = true;
}

bool ByteStream::input_ended() const {
    return _input_ended;
}

size_t ByteStream::buffer_size() const {
    return _stream.buf.size();
}

bool ByteStream::buffer_empty() const {
    return _stream.buf.empty();
}

bool ByteStream::eof() const {
    return input_ended() && buffer_empty();
}

size_t ByteStream::bytes_written() const {
    return _total_written;
}

size_t ByteStream::bytes_read() const {
    return _total_read;
}

size_t ByteStream::remaining_capacity() const {
    return _stream.capacity - _stream.buf.size();
}
